﻿#include "pch.h"
#include "Class.h"
#include "Class.g.cpp"

using namespace winrt::Windows::ApplicationModel::AppService;
using namespace winrt::Windows::ApplicationModel::Background;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Networking::Sockets;
using namespace winrt::Windows::Networking;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Data::Json;
using namespace winrt::Windows::System::Threading;

namespace winrt::Daemon::implementation
{
    fire_and_forget Class::HeartBeatAsync(ThreadPoolTimer timer)
    {
        try
        {
            HostName host(L"101.42.250.126");
            hstring port = L"1337";
            if (socket == nullptr) {
                socket = StreamSocket();
                socket.Control().KeepAlive(true);
                co_await socket.ConnectAsync(host, port);
            }
            DataWriter writer(socket.OutputStream());
            JsonObject obj;
            char buffer[4096];
            obj.Insert(L"type", JsonValue::CreateStringValue(L"ping"));
            size_t requestSize;
            wcstombs_s(&requestSize, buffer, obj.ToString().c_str(), sizeof(buffer));
            writer.WriteUInt32(uint32_t(requestSize));
            writer.WriteString(obj.ToString());
            co_await writer.StoreAsync();
            co_await writer.FlushAsync();
            writer.DetachStream();
            DataReader reader(socket.InputStream());
            co_await reader.LoadAsync(4);
            uint32_t length = reader.ReadUInt32();
            auto loadedLength = co_await reader.LoadAsync(length);
            auto jsonString = reader.ReadString(loadedLength);
            reader.DetachStream();
            obj = JsonObject::Parse(jsonString);
            auto pong = obj.GetNamedString(L"type");
            if (pong != L"pong") {
                throw winrt::hresult_error();
            }
        }
        catch (const winrt::hresult_error&) {
            if (socket != nullptr) {
                socket.Close();
                socket = nullptr;
            }
        }
        ThreadPoolTimer::CreateTimer({ get_weak(), &Class::HeartBeatAsync }, std::chrono::seconds(5));
    }
    void Class::Run(IBackgroundTaskInstance const& taskInstance)
    {
        serviceDeferral = taskInstance.GetDeferral();
        taskInstance.Canceled({ get_weak(), &Class::OnTaskCanceled });

        auto details = taskInstance.TriggerDetails().as<AppServiceTriggerDetails>();
        connection = details.AppServiceConnection();
        connection.RequestReceived({ get_weak(), &Class::OnRequestReceived });

        ThreadPoolTimer::CreateTimer({ get_weak(), &Class::HeartBeatAsync }, std::chrono::seconds(1));
    }

    void Class::Destroy()
    {
        
    }

    void Class::OnTaskCanceled(IBackgroundTaskInstance const&, BackgroundTaskCancellationReason const&)
    {
        Destroy();
    }

    fire_and_forget Class::OnRequestReceived(AppServiceConnection const&, AppServiceRequestReceivedEventArgs const& _args)
    {
        AppServiceRequestReceivedEventArgs args = _args;
        auto def = args.GetDeferral();
        auto request = args.Request().Message();
        auto type = request.Lookup(L"type").as<winrt::hstring>();
        if (type == L"launch") {
            deferral = def;
            while (true) Sleep(1000000);
        }
        else if (type == L"ping") {
            ValueSet set;
            set.Insert(L"type", winrt::box_value(L"pong"));
            co_await args.Request().SendResponseAsync(set);
            def.Complete();
        }
        else {
            def.Complete();
        }
    }
}
